package com.lilosoft.core.aop;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;
import java.util.TimerTask;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.NamedThreadLocal;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import com.lilosoft.core.annotation.AppCode;
import com.lilosoft.core.annotation.MethodLog;
import com.lilosoft.core.executor.ThreadPoolManager;
import com.lilosoft.core.executor.log.LogTaskFactory;
import com.lilosoft.core.utils.HttpUtil;
import com.lilosoft.core.utils.commons.DateUtils;

/**
 * 日志记录切面
 * @author qiusheng
 * @version 2018-05-25
 */
@Aspect
@Component
public class LogAspect {

    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    private static final ThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<Long>("ThreadLocal StartTime");

    @Pointcut(value = "@annotation(com.lilosoft.core.annotation.MethodLog)")
    public void cutMethod() {}
    
    @Before("cutMethod()")
    public void before(JoinPoint joinPoint) {
        if (logger.isDebugEnabled()){
			long beginTime = System.currentTimeMillis();//1、开始时间  
	        startTimeThreadLocal.set(beginTime);		//线程绑定变量（该数据只有当前请求的线程可见）  
	        Date beginTimeDT = new Date(beginTime);
	        logger.debug("开始计时: {}", DateUtils.formatDate(beginTimeDT, "hh:mm:ss.SSS"));
		}
    }
    
    /**
     * 打印JVM信息
     */
    private void printJVMInfo() {
		long beginTime = startTimeThreadLocal.get();//得到线程绑定的局部变量（开始时间）  
		long endTime = System.currentTimeMillis(); 	//2、结束时间  
		if (logger.isDebugEnabled()){
			Date endTimeDT = new Date(endTime);
			logger.debug("计时结束：{}  耗时：{}  最大内存: {}m  已分配内存: {}m  已分配内存中的剩余空间: {}m  最大可用内存: {}m",
	        		DateUtils.formatDate(endTimeDT, "hh:mm:ss.SSS"), 
	        		DateUtils.formatDateTime(endTime - beginTime),
					Runtime.getRuntime().maxMemory()/1024/1024, 
					Runtime.getRuntime().totalMemory()/1024/1024, 
					Runtime.getRuntime().freeMemory()/1024/1024, 
					(Runtime.getRuntime().maxMemory()-Runtime.getRuntime().totalMemory()+Runtime.getRuntime().freeMemory())/1024/1024); 
		}
    }
    
    /**
     * 正常
     * @param point
     */
    @AfterReturning(pointcut="cutMethod()")
    public void afterReturning(JoinPoint point) {
    	printJVMInfo();
    	handle(point, null);
    }

    /**
     * 异常
     * @param point
     * @param exception
     */
    @AfterThrowing(pointcut="cutMethod()", throwing="exception", argNames="exception")
    public void afterThrowing(JoinPoint point, Exception exception) {
    	printJVMInfo();
        handle(point, exception);
    }
    
    /**
     * 启动日志记录
     * @param point
     * @param exception
     */
    private void handle(JoinPoint point, Exception exception){
    	try {
//	        String arr =     point.getArgs()[0].toString();
//	        Map<String,Object> mapTypes = JSON.parseObject(arr);  
//	        mapTypes.get("");
 	      // logger.info("日志信息", point.getDeclaringTypeName().getName());

    		// 如果当前用户未登录
//    		ShiroUser user = ShiroKit.getUser();
//    		if (null == user) {
//    			return;
//    		}
    		long beginTime = startTimeThreadLocal.get();//得到线程绑定的局部变量（开始时间）  
    		long endTime = System.currentTimeMillis(); 	//2、结束时间 
    		// 获取拦截的方法名
    		Signature sig = point.getSignature();
    		MethodSignature msig = null;
    		if (!(sig instanceof MethodSignature)) {
    			throw new IllegalArgumentException("该注解只能用于方法");
    		}
    		msig = (MethodSignature) sig;
    		Object target = point.getTarget();
    		Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
    		
    		String moduleName = "";
    		AppCode appCode = AnnotationUtils.findAnnotation(currentMethod.getDeclaringClass(), AppCode.class);
    		if(appCode != null) {
    			moduleName = appCode.moduleName();
    		}
    		
    		String className = currentMethod.getDeclaringClass().getName();
    		String methodName = currentMethod.getName();
    		
    		MethodLog annotation = currentMethod.getAnnotation(MethodLog.class);
    		String remark = annotation.remark();

    		Map<String, String[]> params = HttpUtil.getRequest().getParameterMap();
    		Object[] args = point.getArgs();
    		
    		String remoteHost = HttpUtil.getIp();
    	    LogTaskFactory.bussinessLog(endTime-beginTime,exception,remoteHost, moduleName, className, methodName, remark, params, args);
    		 
    	} catch (Exception e) {
    		e.printStackTrace();
        }
    }
}